
#include "Converter.h"

Converter::Converter(const char* soapFileName, 
			const unsigned int readLength) {
	_soapFile = new ifstream(soapFileName, ifstream::in);	
	_readLength = readLength;
} 

Converter::~Converter() {
	_soapFile->close();
}

void Converter::getMismatch(const char* str, unsigned char* offset, signed char* sub) {
	char buf[10] = {0};
	int n = 0;
	for(int i = 3;;i++) {
		if(str[i] >= '0' && str[i] <= '9') {
			buf[n] = str[i];
			n++;
		} else {
			break;
		}
	}

	*offset = atoi(buf);
	*sub = str[3 + n];
}

void Converter::filterAlign(const char* outFileName) {
	ofstream outFile(outFileName);
	Soap_format soap;
	unsigned int numAlign = 0;
	
	//1. get the numAlign and readLength
	for(std::string line; getline(*_soapFile, line);) {
		std::istringstream s(line);
		if((s>>soap) && (soap.get_read_len() == _readLength)) {
			outFile << line << endl;
			numAlign += 1;
		}
	}
	outFile.close();
	cout << "Alignments filtering done, there are " << numAlign << " alignments." << endl;
}

void Converter::soap2bin(AlignResult* alignResult, const unsigned int qid, Soap_format* soap) {
	char* qscore = alignResult->get_qscore(qid);
	for(unsigned int i = 0; i < _readLength; i++) {
		qscore[i] = soap->get_qual(i);
	}
	alignResult->_pos[qid] = soap->get_pos();
	alignResult->_hit[qid] = soap->get_hit();
	alignResult->_strand[qid] = soap->strand;

	if(soap->get_mismatch() == 0) {
		alignResult->_sub[qid].x = -1;
		alignResult->_sub[qid].y = -1;
	} else if (soap->get_mismatch() == 1) {
		getMismatch(soap->mis1.c_str(), &(alignResult->_offset[qid].x), 
			&(alignResult->_sub[qid].x));
		alignResult->_sub[qid].y = -1;
	} else if(soap->get_mismatch() == 2) {
		getMismatch(soap->mis1.c_str(), &(alignResult->_offset[qid].x), 
			&(alignResult->_sub[qid].x));
		getMismatch(soap->mis2.c_str(), &(alignResult->_offset[qid].y), 
			&(alignResult->_sub[qid].y));
	} else {
		cout << "!!!ERROR: #mismatch exceeds 2" << endl;
		exit(-1);
	}
}

void Converter::run(const char* outFileName, 
		const unsigned int refLength, 
		const unsigned int winSize, 
		const unsigned int bucketSize) {
	Soap_format soap;
	
	//1. get the chrName, numAlign and readLength
	unsigned long numAlign = 0;
	string chrName;
	unsigned int count = 0;
	for(std::string line; getline(*_soapFile, line);) {
		std::istringstream s(line);
		if(s>>soap) {
			if (soap.get_read_len() == _readLength) {
				numAlign += 1;
				chrName = soap.get_chr_name();
			} else {
				cout << "!!!WARNING: the alignment has a different length: " << 
					count << endl;
				cout << _readLength << ", " << soap.get_read_len() << endl;
			}
		} else {
			cout << line << endl;
			cout << "!!!WARNING: the alignment is not parsed correctly: " << 
				count << endl;
		}
		++count;
	}
	cout << "numAlign = " << numAlign << endl;
	cout << "readLength = " << _readLength << endl;
	
	//2. allocate the memory
	AlignResult* alignResult = new AlignResult(chrName, numAlign, _readLength);

	//3. parse into the data structure
	_soapFile->clear();
	_soapFile->seekg(0);
	count = 0;
	char* qscore = NULL;
	for(std::string line; getline(*_soapFile, line);) {
		std::istringstream s(line);
		if((s>>soap) && (soap.get_read_len() == _readLength)) {
			soap2bin(alignResult, count, &soap);
			count++;
		}
	}

	//4. dump into the disk
	AlignResultWrite* write = new AlignResultWrite(chrName, _readLength, refLength,
                        		winSize, bucketSize, string(outFileName));
	write->insert(alignResult);
	write->close();

	delete alignResult;
}

void Converter::cleanSoap(string outFileName, string soapFileName, 
				string refFileName, const int readLength) {
	/*read the reference*/
	Sequence* ref = new Sequence();
	ref->read(refFileName.c_str());

	/*read and clean the soap file*/
	unsigned int count = 0;
	ifstream soapFile(soapFileName.c_str(), ifstream::in);
	ofstream outFile(outFileName.c_str());
	Soap_format soap;
	char* buf = new char[readLength];
	for(std::string line; getline(soapFile, line);) {
		std::istringstream s(line);
		if((s>>soap) && (soap.get_read_len() == readLength)) {
			//copy the reference
			for(int i = 0; i < readLength; i++) {
				buf[i] = toupper(ref->base[soap.get_pos() + i]);
			}

			//clean the mismatch information
			uchar2 offset;
			char2 sub;
			if(soap.get_mismatch() == 0) {
			} else if (soap.get_mismatch() == 1) {
				getMismatch(soap.mis1.c_str(), &(offset.x), &(sub.x));
				buf[offset.x] = sub.x;
			} else if(soap.get_mismatch() == 2) {
				getMismatch(soap.mis1.c_str(), &(offset.x), &(sub.x));
				getMismatch(soap.mis2.c_str(), &(offset.y), &(sub.y));
				buf[offset.x] = sub.x;
				buf[offset.y] = sub.y;
			} else {
				cout << "!!!ERROR: #mismatch exceeds 2" << endl;
				exit(-1);
			}

			//the read cannot contain 'N'
			if(hasN(buf, readLength)) {
				continue;
			}
		
			//copy the clean read, with the strictly correct mismatch	
			for(int i = 0; i < readLength; i++) {
				soap.read[i] = buf[i];
			}

			//now output the clean alignment
			outFile << soap << endl;
		}
	}

	soapFile.close();
	outFile.close();

	ref->clear();
	delete[] buf;
}


